package com.pf.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.codec.binary.Base64;

import javax.crypto.Cipher;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * RSA 加解密：本案例未使用分段处理，如果明文长度超过117字节，会报错（javax.crypto.IllegalBlockSizeException: Data must not be longer than 117 bytes）
 * 在 RSAUtils2中有分段加解密处理
 * <p>
 * RSA加密对明文的长度有所限制，规定需加密的明文最大长度=密钥长度-11（单位是字节，即byte），所以在加密和解密的过程中需要分块进行。
 * 而密钥默认是1024位，即1024位/8位-11=128-11=117字节。所以默认加密前的明文最大长度117字节，解密密文最大长度为128字。
 * 那么为啥两者相差11字节呢？是因为RSA加密使用到了填充模式（padding），即内容不足117字节时会自动填满，用到填充模式自然会占用一定的字节，
 * 而且这部分字节也是参与加密的
 * <p>
 * 使用512bit的RSA加密时，明文长度不能超过53字节
 *
 * @author PanFei
 * @version 1.0.0
 * @createTime 2022/2/13 17:00
 */
@Slf4j
public class RSAUtils {

    public static final String PUBLIC_KEY_1024 = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDLTY4n3Ee4juxH+LGnr/c7nGXa3pfUpueXUbejYzP90D06VoWoN6grjtYtNr5Jwmy9j6EpvxlzdeCEEU03WZmwpKmXnn8K/cS6aC9MijZntIK2GCxuzYe2WmfBZa9YeACQDjxTEVVdOTQfJvXMv8sS2cE6xkG93xnFmgGvmhhNIwIDAQAB";
    public static final String PRIVATE_KEY_1024 = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMtNjifcR7iO7Ef4saev9zucZdrel9Sm55dRt6NjM/3QPTpWhag3qCuO1i02vknCbL2PoSm/GXN14IQRTTdZmbCkqZeefwr9xLpoL0yKNme0grYYLG7Nh7ZaZ8Flr1h4AJAOPFMRVV05NB8m9cy/yxLZwTrGQb3fGcWaAa+aGE0jAgMBAAECgYA4j44iJtXJUfIIn/cFGR6mdf+cYdtJ7Vl3LuPxT/MyBRJTvaiXwUxHOgj6MUXFjZLlNiV3UNRK3BungozB/mqUTET3iGrw4Qj8kzZv869oCyrs1xeV04bLhSF7p2MpfnHKfPAN+NWVNC5/9/IDqmMxqRjJBINMuMQmm01MwmWvUQJBAPGhPsU83JIBf4xzPbfga0RLOzF/Sr72KvE4FnOgQczO6WQq1ihSuH5UjsdxIfJotgBSKZzUALA4ZVFj1suoI4sCQQDXZMrWf/u8M8/aYvRo5qKHdELdhBZvorq+C/raCcwFYs7EioEKJvAEsDB01PhbMewg2fvQ7VXmhH49W4E55c/JAkB0mGI5usyUh7SMiW9WsywPoF3H3KysyQyfqXjsAoC923ozo5gDjScJjrP2+VhwKKbqMLtzubk7T3tUcuDLFS9RAkEAsSXIXRntZCAItsfj09K7oUPRKnZULYRcqakYqvKbxWI6RBLVhg/4HyL4912g1P/+ABbHsFFB8NDPMo9rfL6xYQJAGkwxxE1tT6Yqv6Px8tdHWxFdW04oBQxxkekdtp8A4/effW1vtt0yjZoN1s5NzevXmZFrKTvhp29y+pOFvHyszQ==";
    public static final String PUBLIC_KEY_2048 = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuDN0eQwcT461erv6IyrChMUidy1gwM7QnATqWK7HA66JZA/iuQLtEOJmjDfPSJiYRidXgnWJNGOH/kvMYM2ZovhVjoWDiBVGe5MpGlW/NXtT8HBb000QkwjizFdVtgcFiONCuudat5V5pJ8qthGMOoaMQv/Rcn3ajPOsbCILatGgeaKZHIrWHW0CGD1fpNhFdQ4zaomrhlbZQX5lOn/dsfieD2QRhDzJI57K6r0JJKsZr/kLrZO0JSjWS+ju22FAp392Wj6FfNSnMp23O+/JZoXk6AtwdUEkaO819SOINtMlQIH6nKZVKXl5ovLttDjBe8NPqchQdMvf56ukP73JUwIDAQAB";
    public static final String PRIVATE_KEY_2048 = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC4M3R5DBxPjrV6u/ojKsKExSJ3LWDAztCcBOpYrscDrolkD+K5Au0Q4maMN89ImJhGJ1eCdYk0Y4f+S8xgzZmi+FWOhYOIFUZ7kykaVb81e1PwcFvTTRCTCOLMV1W2BwWI40K651q3lXmknyq2EYw6hoxC/9FyfdqM86xsIgtq0aB5opkcitYdbQIYPV+k2EV1DjNqiauGVtlBfmU6f92x+J4PZBGEPMkjnsrqvQkkqxmv+Qutk7QlKNZL6O7bYUCnf3ZaPoV81Kcynbc778lmheToC3B1QSRo7zX1I4g20yVAgfqcplUpeXmi8u20OMF7w0+pyFB0y9/nq6Q/vclTAgMBAAECggEABdCyC6RDPIu/1PkcQ4H1FCX5GZQYKt4cneBV2aji5Y3cDoml2btq6o6LOEAGzj7rB8P0Mk0toLbHs1KHSEMVZ5y5br9uUJyDViFmOcudNr4G/188tuYG8O1l45eaQB3modSy5Z2qMoOIjJbtzUOujqZyxSa4YQnyHWMP/XXxsFSSHjUjmbaw2lKw3KqbhShs9LJwwC0LCVzPvHbMNoooklv67AaIXh/VN8tLLsO/cF0N1/Sp/XVfnzhy1oAClfILWQJtP7luQfSuUJ3csj8uppobScxGLZAI290WOIO/62i9yT4jz38GrgZcOD4d3OiLMgeAjhe4YfkL3vvopE6diQKBgQDswCr5v5QEVomf6CjFID7jGM7gYn+O3XVmG0crRn5jvVFasrgQ3MJOf1J8f9DSZQi1IWtgvFQUiIvJUZKfQUFQVGTSxcb+geznB0dHVIqzL3dceeqzJkMPe2kipT/jgY6XrwoqRVJPo5wU6myTuVJbY1gi8B6MrWedrh4HqX2VjQKBgQDHLX7Smyo4uDh83ptylPA6wEwDZVu/kEblsLqLkvRY2vkDw2+yX+/bkcx+qmfL1Ls8ttO002Jhy96+qY83et3Uenp/rd8XsqdgfPp3UHUvPEiTk+uJu5otr8nepuA/uUTmaMUD+2qE1gmossqjWYEM0HHIrUGGMtxry3WnctTyXwKBgQDC0D6M1IBQMYV99+PUkfwtkzRon6SnQsRgCQI/Mfq+yAIxx+5+samSPmL535NODaVkpuNc7puhadrdBirIsUG1gDyW0/gzZWYzlWGp6H9Zgy2NAyWF1W5JRxv0TlaMNwfG+Vo7/l8E+XRuIZjG1jBW0yCrXztEN0yUC7raQzX74QKBgQCqEAFqJ0wck8a/b36AjgSz1T7R43g8Dx9JvOT6iWYSfB0EBpnXTqzI6CsXt73aPvADvgLzMU2ePOkaGEKNV14SruTrIXULkeHxs6RTvaHgy/MX9sO0nTbebWrKredo4XKf3LLjXIatluc2YDo+0Qwr+co9UxlQURr8+wKhD54ctwKBgQCmxQ5fgpKrhDwJXhi5i1Jtq8C3HzDvapqhDEazk2K7+Uo3osd0MGIKmBUJXhRIfVuYRtwwFu8uqi3uKlPdo8cxvRTmSOYqiPXVRVsmMU1cQOKZML4Iesp28rAjNkZOzTaVoNH9r0EelM57jo9HKPUXYHqK1iHkedq/XgnwtSSpbQ==";


    public static void main(String[] args) throws Exception {
        printPublicKeyAndPrivateKey();
        /**
         * 测试1：
         * 测试密钥长度 1024，明文超过 117 bytes，会抛出 javax.crypto.IllegalBlockSizeException: Data must not be longer than 117 bytes
         * 本测试的 oriStr长度为 118 bytes。在 RSAUtils2 案例中已解决该问题
         */
//        String oriStr = "测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测1";
//        String encryptStr = encryptByRSA(oriStr, PUBLIC_KEY_1024);
//        String decryptStr = decryptByRSA(encryptStr, PRIVATE_KEY_1024);

        /**
         * 测试2：
         * 测试密钥长度 2048，明文超过 222 bytes，会抛出 Exception in thread "main" javax.crypto.IllegalBlockSizeException: Data must not be longer than 222 bytes
         * 本测试的 oriStr长度为 223 bytes。在 ERSAUtils 案例中已解决该问题
         */
        String oriStr = "测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试1";
        String encryptStr = encryptByRSA2(oriStr, PUBLIC_KEY_2048);
        String decryptStr = decryptByRSA2(encryptStr, PRIVATE_KEY_2048);
        System.out.println("原字符串=" + oriStr);
        System.out.println("加密字符串=" + encryptStr);
        System.out.println("解密字符串=" + decryptStr);
    }

    /**
     * 获取RSA的公钥和私钥
     *
     * @return
     */
    private static void printPublicKeyAndPrivateKey() {
        try {
            //1.初始化密钥
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            keyPairGenerator.initialize(2048);
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            //获取公钥和私钥
            PublicKey aPublic = keyPair.getPublic();
            PrivateKey aPrivate = keyPair.getPrivate();
            String publicKeyStr = Base64.encodeBase64String(aPublic.getEncoded());
            String privateKeyStr = Base64.encodeBase64String(aPrivate.getEncoded());
            System.out.println("公钥：" + publicKeyStr);
            System.out.println("私钥：" + privateKeyStr);
        } catch (Exception e) {
            log.error("RsaUtils - getPublicKeyAndPrivateKey error", e);
        }
    }

    /**
     * 获取密钥对
     *
     * @return 密钥对
     */
    public static KeyPair getKeyPair() throws Exception {
        KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
        generator.initialize(1024);
        return generator.generateKeyPair();
    }

    /**
     * 获取私钥
     *
     * @param privateKey 私钥字符串
     * @return
     */
    public static PrivateKey getPrivateKey(String privateKey) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        byte[] decodedKey = Base64.decodeBase64(privateKey.getBytes());
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodedKey);
        return keyFactory.generatePrivate(keySpec);
    }

    /**
     * 获取公钥
     *
     * @param publicKey 公钥字符串
     * @return
     */
    public static PublicKey getPublicKey(String publicKey) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        byte[] decodedKey = Base64.decodeBase64(publicKey.getBytes());
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decodedKey);
        return keyFactory.generatePublic(keySpec);
    }


    /**
     * 加密
     *
     * @param oriStr
     * @param publicKeyStr
     * @return
     */
    public static String encryptByRSA(String oriStr, String publicKeyStr) throws Exception {
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyStr));
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] result = cipher.doFinal(oriStr.getBytes());
        return Base64.encodeBase64String(result);
    }

    /**
     * 解密
     *
     * @param encryptStr
     * @param privateKeyStr
     * @return
     */
    public static String decryptByRSA(String encryptStr, String privateKeyStr) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyStr));
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        return new String(cipher.doFinal(Base64.decodeBase64(encryptStr)));
    }


    /**
     * RSA/ECB/OAEPwithMD5andMGF1Padding 加密
     *
     * @param oriStr
     * @param publicKeyStr
     * @return
     */
    public static String encryptByRSA2(String oriStr, String publicKeyStr) throws Exception {
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyStr));
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
        Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPwithMD5andMGF1Padding");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] result = cipher.doFinal(oriStr.getBytes());
        return Base64.encodeBase64String(result);
    }

    /**
     * RSA/ECB/OAEPwithMD5andMGF1Padding 解密
     *
     * @param encryptStr
     * @param privateKeyStr
     * @return
     */
    public static String decryptByRSA2(String encryptStr, String privateKeyStr) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyStr));
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
        Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPwithMD5andMGF1Padding");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        return new String(cipher.doFinal(Base64.decodeBase64(encryptStr)));
    }
}
